#! /usr/bin/env python
# coding=utf-8
# ******************************************************************************
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: Lostway
# Create: 2020-08-19
# ******************************************************************************/
"""
rpm包检查模块
"""

import os
import re
import subprocess
import errno
import argparse
from autoimport.logmodule import logger

# system configuration
APP_NAME = "RPM_verify"

log = logger.init(APP_NAME)


def main():
    """
    主函数
    :return: 无
    """
    parser = argparse.ArgumentParser()
    parser.add_argument("-p1", "--new_rpm_path", default="/home/lostway/cao/home/abuild/rpmbuild/RPMS/aarch64",
                        nargs="?", required=True,
                        help="set the path of new rpm.")

    parser.add_argument("-p2", "--old_rpm_path", default="/home/lostway/cao/home/abuild/rpmbuild/RPMS/aarch64",
                        nargs="?", required=True,
                        help="set the path of old rpm.")

    parser.add_argument("-n", "--name", default="rubygem-nio4r", nargs="?",
                        help="set the name of new rpm.")
    parser.add_argument("-d", "--delete", action="store_true",
                        help="args switch to delete *debug*.rpm")

    args = parser.parse_args()

    new_rpm_path = args.new_rpm_path
    old_rpm_path = args.old_rpm_path
    is_delete_debug = args.delete
    name = args.name
    if not valid(new_rpm_path):
        log.error("Dir [%s] not exist." % new_rpm_path)
        return
    if not valid(old_rpm_path):
        log.error("Dir [%s] not exist." % old_rpm_path)
        return

    if is_delete_debug:
        delete_debug_rpm(new_rpm_path)
        delete_debug_rpm(old_rpm_path)

    new_list_path, new_list = get_file_names(new_rpm_path)
    old_list_path, old_list = get_file_names(old_rpm_path)
    cur_path = os.getcwd()
    for p in new_list_path:
        data = run_rpm_command("cp " + p + " " + cur_path)

    for p in old_list_path:
        data = run_rpm_command("cp " + p + " " + cur_path)

    TruncateFile(cur_path, "Log.txt")
    # check rpm set
    do_check_rpm_set("rpm -pql ", new_list, old_list, "check rpm set")
    # check rpm provides set
    do_check_rpm_set("rpm -pql --provides ", new_list, old_list, "check rpm provides set")
    # upgrade and uninstall
    upgrade_and_uninstall(new_list, old_list)
    # check function
    # check_function(new_list)
    # delete cur rpm
    delete_cur_rpm(cur_path)


def auto_main(old_rpm_path, new_rpm_path, rpm_name):
    """
    抓函数
    :param old_rpm_path: 老包路径
    :param new_rpm_path: 新包路径
    :param rpm_name: 包名
    :return: 1-失败，0-成功
    """
    logger.add_file_handler(log, rpm_name, "rpm_check")
    if not valid(new_rpm_path):
        log.error("Dir [%s] not exist." % new_rpm_path)
        return 1
    if not valid(old_rpm_path):
        log.error("Dir [%s] not exist." % old_rpm_path)
        return 1

    new_list_path, new_list = get_file_names(new_rpm_path)
    old_list_path, old_list = get_file_names(old_rpm_path)
    cur_path = os.path.dirname(os.path.abspath(__file__))
    os.chdir(cur_path)
    for p in new_list_path:
        run_rpm_command("cp " + p + " " + cur_path)

    for p in old_list_path:
        run_rpm_command("cp " + p + " " + cur_path)

    TruncateFile(cur_path, "Log.txt")
    # check rpm set
    check_set_result1 = do_check_rpm_set("rpm -pql ", new_list, old_list, "check rpm set")
    # check rpm provides set
    check_set_result2 = do_check_rpm_set("rpm -pql --provides ", new_list, old_list, "check rpm provides set")
    # upgrade and uninstall
    check_install_result = upgrade_and_uninstall(new_list, old_list)
    # check function
    # check_function(new_list)
    # delete cur rpm
    delete_cur_rpm(cur_path)
    if check_set_result1 == 0 and check_set_result2 == 0 and check_install_result == 0:
        return 0
    else:
        return 1


def delete_cur_rpm(path):
    """
    删除当前rpm包
    :param path: 包路径
    :return: 无
    """
    cmd = "rm -rf " + path + "/*.rpm"
    _, _ = run_rpm_command(cmd)
    return


def delete_debug_rpm(path):
    """
    delete path/*debug*.rpm
    :param path: 包路径
    :return: 输出+错误码
    """
    cmd = "rm -rf " + path + "/*debug*.rpm"
    try:
        proc_new = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    except OSError as err:
        if err.errno == errno.ENOENT:
            log.error("Command %s not found." % cmd)
        return None, None
    stdout_new, stderr_new = proc_new.communicate()
    returncode = proc_new.returncode
    proc_new.poll()

    return stdout_new.decode('utf-8', 'ignore'), returncode


def do_check_rpm_set(command, list1, list2, comment):
    """
    check rpm set
    :param command: 命令
    :param list1: 包列表
    :param list2: 包列表
    :param comment: 注释信息
    :return: 1-失败，0-成功
    """
    with open("Log.txt", 'a') as f:
        f.write(
            "============================================= " + comment +
            " =============================================" + '\n')
        log.info(
            "============================================= " + comment +
            " =============================================")
        result = check_rpm_set(command, list1, list2)
        if result is True:
            f.write(
                "============================================= " + comment +
                " Succeed! =============================================" + '\n')
            f.write('\n')
            log.info(
                "============================================= " + comment +
                " Succeed! =============================================")
            return 0
        else:
            f.write(
                "============================================= " + comment +
                " Failed! =============================================" + '\n')
            f.write('\n')
            log.error(
                "============================================= " + comment +
                " Failed! =============================================")
            return 1


def get_file_names(path):
    """
    获取文件名称
    :param path: 路径
    :return: 包名+路径
    """
    data_path = []
    data_name = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            continue
        if not file.endswith('rpm'):
            continue
        if 'debug' not in file and 'src' not in file:
            data_path.append(os.path.join(path, file))
            data_name.append(file)
    return data_path, data_name


def get_rpm_name(list1):
    """
    获取rpm包名
    :param list1: 列表
    :return: 包名列表
    """
    pattern = r"(.*?)-([0-9._-]+)-([a-zA-Z0-9._-]+)\.(\b\w+\b)"
    re_match = re.compile(pattern)
    name_list = []
    for element in list1:
        tmp = re_match.match(element)
        if tmp is not None:
            name_list.append(tmp.group(1))
    return name_list


def valid(path):
    """
    判断路径是否有效
    :param path: 路径
    :return: True-合法，False-非法
    """
    if os.path.exists(path):
        return True
    else:
        return False


def check_rpm_set(command, list1, list2):
    """
    检查rpm包
    :param command: 检查项命令
    :param list1: 包列表1
    :param list2: 包列表2
    :return: True-成功，False-失败
    """
    data1, _ = run_rpm_command(command, list1)
    data2, _ = run_rpm_command(command, list2)
    f = open("Log.txt", 'a')

    if data1 is None:
        f.write("Command not found." + '\n')
        log.warning("Command not found.")
        f.close()
        return False
    data1 = data1.split('\n')
    data2 = data2.split('\n')
    inter_list = list(set(data1).intersection(set(data2)))
    if len(inter_list) != len(data1) or len(inter_list) != len(data2):
        log.error('The rpm set is not consistent for [%s].' % command)
        data1_ex = list(set(data1).difference(set(inter_list)))
        data2_ex = list(set(data2).difference(set(inter_list)))
        f.write("The rpm is not consistent, below is the difference" + '\n')
        log.info("The rpm is not consistent, below is the difference")
        filter_version_set1 = set()
        f.write("new rpm: " + '\n')
        log.info("new rpm: ")
        for d in data1_ex:
            f.write(d + '\n')
            log.info(d)
            filter_version_set1.add(d.split("=")[0])
        filter_version_set2 = set()
        f.write("old rpm: " + '\n')
        log.info("old rpm: ")
        for d in data2_ex:
            f.write(d + '\n')
            log.info(d)
            filter_version_set2.add(d.split("=")[0])
        f.close()
        filter_version_diff = filter_version_set1.difference(filter_version_set2)
        if len(filter_version_diff) > 0:
            return False
        return True
    else:
        f.close()
        return True


def check_function(list1):
    """
    执行检查的函数
    :param list1: 包列表
    :return: 无
    """
    f = open("Log.txt", 'a')
    f.write(
        "============================================= check function "
        "=============================================" + '\n')
    log.info(
        "============================================= check function "
        "=============================================")
    command = "rpm -qpl --provides "
    command1 = " grep /usr/bin"
    data, code = run_rpm_command(command, list1, command1)
    data = data.split('\n')
    for line in data:
        result, code = run_rpm_command(line + " --help")
        f.write("======================= check " + '[' + line + ']' + '\n')
        log.info("======================= check " + '[' + line + ']')
        f.write(result + '\n')
        log.info(result)
    f.write(
        "============================================= check function end "
        "=============================================" + '\n')
    log.info(
        "============================================= check function end "
        "=============================================")
    f.close()


def upgrade_and_uninstall(list1, list2):
    """
    升级与卸载
    :param list1: 包列表1
    :param list2: 包列表2
    :return: 0-成功，1-失败
    """
    with open("Log.txt", 'a') as f:
        f.write(
            "============================================= upgrade and uninstall "
            "=============================================" + '\n')
        log.info(
            "============================================= upgrade and uninstall "
            "=============================================")
    install_result = verify_help('rpm -ivh ', list2, "install old rpm")
    upgrade_result = verify_help('rpm -Uvh ', list1, "upgrade old rpm")

    check_function(list1)
    name_list = get_rpm_name(list1)

    # help('rpm -e ', list1, "uninstall rpm")
    uninstall_result = verify_help('rpm -e ', name_list, "uninstall rpm")

    # help('rpm -ivh ', list1, "install new rpm")

    # help('rpm -e ', list1, "uninstall new rpm")
    # help('rpm -e ', name, "uninstall new rpm")

    with open("Log.txt", 'a') as f:
        f.write(
            "============================================= upgrade and uninstall end "
            "=============================================" + '\n')
        log.info(
            "============================================= upgrade and uninstall end "
            "=============================================")
    if install_result == 0 and upgrade_result == 0 and uninstall_result == 0:
        return 0
    else:
        return 1


def verify_help(command, data, comment):
    """
    帮助信息打印
    :param command: 执行明亮
    :param data: 执行包
    :param comment: 注释信息
    :return: 0-成功，1-失败
    """
    with open("Log.txt", 'a') as f:
        data, code = run_rpm_command(command, data)
        f.write("===================== " + comment + '\n')
        f.write(data + '\n')
        log.info("===================== " + comment)
        log.info(data)
        if code != 0:
            f.write("===================== " + comment + " Failed!" + '\n')
            log.info("===================== " + comment)
            return 1
        else:
            f.write("===================== " + comment + " Succeed!" + '\n')
            log.info("===================== " + comment + " Succeed!")
            return 0


def run_rpm_command(command, list1=None, command1=None):
    """
    执行rpm包命令
    :param command: 命令
    :param list1: 包列表
    :param command1: 附加命令
    :return: 输出+错误码
    """
    cmd = command
    if list1 is not None:
        if isinstance(list1, list):
            args_new = ' '.join(list1)
        else:
            args_new = list1
        cmd = cmd + args_new

    if command1 is not None:
        cmd = cmd + '|' + command1

    try:
        proc_new = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    except OSError as err:
        if err.errno == errno.ENOENT:
            log.error("Command %s not found." % cmd)
        return None, None
    stdout_new, stderr_new = proc_new.communicate()
    returncode = proc_new.returncode
    proc_new.poll()

    return stdout_new.decode('utf-8', 'ignore'), returncode


def TruncateFile(cur_path, file):
    """
    创建空白文件
    :param cur_path: 文件路径
    :param file: 问价名
    :return: 无
    """
    file_path = os.path.join(cur_path, file)
    if not os.path.exists(file_path):
        os.system(r'touch %s' % file_path)
    with open(file_path, 'r+') as f:
        f.truncate()


if __name__ == "__main__":
    main()
